package main.java.DMDE.persistence.conversion;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import main.java.DMDE.model.*;
import main.java.DMDE.model.Class;
import main.java.DMDE.model.relationships.Association;
import main.java.DMDE.model.relationships.Inheritance;
import main.java.DMDE.model.relationships.RelationshipComponent;
import main.java.DMDE.persistence.DiagramXmlManager;
import main.java.common.IXmlFileManager;
import main.java.common.XmlFileManager;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ERMToDMXmlAdapter {
	
	private IXmlFileManager xmlFileManager = new XmlFileManager();
	private DiagramXmlManager diagramXmlManager = new DiagramXmlManager();
	
	private List<Class> processedClasses = new ArrayList<Class>();
	
	public void convertXml(String ermModelXmlRoute, String dmModelXmlRoute, String diagramName) throws Exception {
	    	String ermRoute =  ermModelXmlRoute + "/" + diagramName + "-comp";
	    	String dmRoute =  dmModelXmlRoute + "/" + diagramName + "-comp";
		Document ermDocument = xmlFileManager.read(ermRoute);
		Element rootElement = ermDocument.getDocumentElement();
		Diagram diagram = new Diagram();
		diagram.setName(dmRoute.substring(dmRoute.lastIndexOf('/')));
		processErmDiagramElement(diagram, rootElement);
		Document dmDocument = this.xmlFileManager.createDocument();
		try {
			Element laPapa = this.diagramXmlManager.getElementFromItem(
				diagram, dmDocument);
			dmDocument.appendChild(laPapa);
			this.xmlFileManager.write(dmDocument, dmRoute);
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			processedClasses = new ArrayList<Class>();
		}
		
	}
	
	private void processErmDiagramElement(Diagram dmDiagram, Element ermDiagramElement) {
		NodeList aux = ermDiagramElement.getElementsByTagName("entities");
		if (aux == null)
			return;
		NodeList entities = aux.item(0) != null ? aux.item(0).getChildNodes() : null;
		if (entities != null) for (int i = 0; i < entities.getLength(); i++) {
			Node node = entities.item(i);
			if (node == null || !(node instanceof Element))
				continue;
			Element entityElement = (Element) node;
			processErmEntityElement(dmDiagram, entityElement);
		}
		aux = ermDiagramElement.getElementsByTagName("relationships");
		if (aux != null) {
			NodeList relationships = aux.item(0) != null ? aux.item(0).getChildNodes() : null;
			if (relationships != null) for (int i = 0; i < relationships.getLength(); i++) {
				Node node = relationships.item(i);
				if (node == null || !(node instanceof Element))
					continue;
				Element relationshipElement = (Element) node;
				processErmRelationshipElement(dmDiagram, relationshipElement);
			}
		}
		aux = ermDiagramElement.getElementsByTagName("hierarchies");
		if (aux != null) {
			NodeList hierarchies = aux.item(0) != null ? aux.item(0).getChildNodes() : null;
			if (hierarchies != null) for (int i = 0; i < hierarchies.getLength(); i++) {
				Node node = hierarchies.item(i);
				if (node == null || !(node instanceof Element))
					continue;
				Element hierarchyElement = (Element) node;
				processErmHierarchyElement(dmDiagram, hierarchyElement);
			}
		}
		aux = ermDiagramElement.getElementsByTagName("subDiagrams");
		if (aux != null) {
			NodeList subdiagrams = aux.item(0) != null ? aux.item(0).getChildNodes() : null;
			if (subdiagrams != null) for (int i = 0; i < subdiagrams.getLength(); i++) {
				Node node = subdiagrams.item(i);
				if (node == null || !(node instanceof Element))
					continue;
				Element subdiagramElement = (Element) node;
				Diagram subDiagram = new Diagram();
				subDiagram.setName(subdiagramElement.getAttribute("name"));
				dmDiagram.addSubDiagram(subDiagram);
				processErmDiagramElement(subDiagram, subdiagramElement);
			}
		}
	}
	
	private void processErmHierarchyElement(Diagram dmDiagram,
			Element hierarchyElement) {
		Class parent = (Class) getEntityById(UUID.fromString(hierarchyElement.getAttribute("generalEntityId")));
		RelationshipComponent parentComponent = new RelationshipComponent();
		parentComponent.setEntity(parent);
		NodeList children = hierarchyElement.getElementsByTagName("specificEntities").item(0).getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node == null || !(node instanceof Element))
				continue;
			Element elem = (Element) node;
			Inheritance inheritance = new Inheritance(dmDiagram);
			inheritance.setId(UUID.randomUUID());
			RelationshipComponent childComponent = new RelationshipComponent();
			childComponent.setEntity(getEntityById(UUID.fromString(elem.getTextContent())));
			inheritance.setComponentA(childComponent);
			inheritance.setComponentB(parentComponent);
			dmDiagram.addRelationship(inheritance);
		}
	}

	private void processErmRelationshipElement(Diagram dmDiagram,
			Element relationshipElement) {
		Class relationshipClass = new Class();
		relationshipClass.setVisibility(Visibility.PUBLIC);
		relationshipClass.setName(relationshipElement.getAttribute("name"));
		relationshipClass.setId(UUID.fromString(relationshipElement.getAttribute("id")));
		processedClasses.add(relationshipClass);
		dmDiagram.addEntity(relationshipClass);
		NodeList entities = relationshipElement.getElementsByTagName("entities").item(0).getChildNodes();
		if (entities != null) for (int i = 0; i < entities.getLength(); i++) {
			Node node = entities.item(i);
			if (node == null || !(node instanceof Element))
				continue;
			Element entityElem = (Element) node;
			Association asoc1 = new Association(dmDiagram);
			asoc1.setId(UUID.randomUUID());
			RelationshipComponent rel = new RelationshipComponent();
			rel.setMinCardinality(getCardinality(entityElem.getAttribute("minimumCardinality")));
			rel.setMinCardinality(getCardinality(entityElem.getAttribute("minimumCardinality")));
			rel.setEntity(getEntityById(UUID.fromString(entityElem.getAttribute("entityId"))));
			RelationshipComponent rel2 = new RelationshipComponent();
			rel2.setEntity(relationshipClass);
			rel2.setMinCardinality(getCardinality(entityElem.getAttribute("maximumCardinality")));
			rel2.setMinCardinality(getCardinality(entityElem.getAttribute("maximumCardinality")));
			
			asoc1.setComponentA(rel);
			asoc1.setComponentB(rel2);
			dmDiagram.addRelationship(asoc1);
		}
		NodeList attributes = relationshipElement.getElementsByTagName("attributes").item(0).getChildNodes();
		if (attributes != null) for (int i = 0; i < attributes.getLength(); i++) {
			Node node = attributes.item(i);
			if (node == null || !(node instanceof Element)) {
				continue;
			}
			Element attributeElem = (Element) node;
			processErmAttribute(relationshipClass, attributeElem);
		}
	}

	private CardinalityValues getCardinality(String attribute) {
		if (CardinalityValues.CERO.getDesc().equals(attribute)) {
			return CardinalityValues.CERO;
		}
		if (CardinalityValues.MANY.getDesc().equals(attribute)) {
			return CardinalityValues.MANY;
		}
		if (CardinalityValues.ONE.getDesc().equals(attribute)) {
			return CardinalityValues.ONE;
		}
		return null;
	}

	private Entity getEntityById(UUID id) {
		for (Entity entity : processedClasses) {
			if (entity.getId().equals(id))
				return entity;
		}
		return null;
	}

	private void processErmEntityElement(Diagram dmDiagram, Element ermEntityElement) {
		Class currentClass = new Class();
		currentClass.setVisibility(Visibility.PUBLIC);
		processedClasses.add(currentClass);
		currentClass.setId(UUID.fromString(ermEntityElement.getAttribute("id")));
		currentClass.setName(ermEntityElement.getAttribute("name"));
		dmDiagram.addEntity(currentClass);
		NodeList attributes = ermEntityElement.getElementsByTagName("attributes").item(0).getChildNodes();
		if (attributes != null) for (int i = 0; i < attributes.getLength(); i++) {
			Node node = attributes.item(i);
			if (node == null || !(node instanceof Element)) {
				continue;
			}
			Element attributeElem = (Element) node;
			processErmAttribute(currentClass, attributeElem);
		}
	}

	private void processErmAttribute(Class currentClass, Element attributeElem) {
		Attribute attribute = new Attribute();
		attribute.setName(attributeElem.getAttribute("name"));
		Type type = new Type();
		String maxCard = attributeElem.getAttribute("maximumCardinality");
		type.setName(maxCard.equals("Infinity") ? "List<String>" : "String");
		attribute.setType(type);
		attribute.setVisibility(Visibility.PRIVATE);
		currentClass.addAttribute(attribute);
	}
}
